﻿using System;
using System.Threading;
using Jiubang.Collections;

namespace Jiubang.Util {
    /// <summary>
    ///   设计为线程安全可共享的随机数发生器。
    ///   内部使用FCL的Random类实现，
    ///   利用对象池避免多线程并发问题。
    /// </summary>
    public static class Randomizer {
        private static readonly LockFreeStack<Random> _stack;
        private static int _count;
        private static readonly int _capacity;
        private static int _hits;
        private static int _misses;

        static Randomizer() {
            _stack = new LockFreeStack<Random>();
            _count = 0;

            //目前认为这个值已经足够大了
            _capacity = 100;
        }

        /// <summary>
        ///   返回不超过指定值的非负随机整数
        /// </summary>
        /// <returns></returns>
        public static int Next(int maxValue) {
            Random random;
            if (_stack.TryPop(out random)) {
                Interlocked.Increment(ref _hits);

                try {
                    return random.Next(maxValue);
                } finally {
                    _stack.Push(random);
                }
            }

            random = new Random();
            Interlocked.Increment(ref _misses);

            var result = random.Next(maxValue);

            if (Interlocked.Increment(ref _count) >= _capacity) {
                Interlocked.Decrement(ref _count);
            } else {
                _stack.Push(random);
            }

            return result;
        }

        /// <summary>
        /// 产生由0开始，指定长度的顺序整数有限行的随机排序结果。
        /// 采用的是抽牌而不是洗牌的模型，在返回非全排列时性能应该比洗牌模式好。
        /// 由于消耗大户rand.next()使用次数相同，到全排列时应该与洗牌模型性能差不多。
        /// </summary>
        /// <param name="sourceSize">要排序的整数长度，生成数组的值域为[0,sourcesize-1]</param>
        /// <param name="destSize">需要在之前指定值域范围内抽取牌的张数</param>
        /// <returns></returns>
        public static int[] RandomIntArray(int sourceSize, int destSize) {
            //防止请求返回量大于原序列量
            if (destSize > sourceSize)
                destSize = sourceSize;
            //防止0长度数组
            if (destSize <= 0)
                return new int[] { };
            var randomRows = new int[destSize];
            var rows = new int[sourceSize];
            int i;
            //写入排序列表序号
            for (i = 0; i < sourceSize; i++)
                rows[i] = i;

            for (i = 0; i < destSize; i++) {
                //返回有数行中的随机行
                int pickedRowNumber = Next(sourceSize - i);
                //插入该行号到新数组
                randomRows[i] = rows[pickedRowNumber];
                //将最后一行有效数据填充已取出的行
                rows[pickedRowNumber] = rows[sourceSize - i - 1];
            }
            return randomRows;
        }
    }
}